﻿using System;
using System.Runtime.InteropServices;
using SCG = System.Collections.Generic;

using Nemerle;
using Nemerle.Assertions;
using Nemerle.Compiler;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;
using TT = Nemerle.Compiler.Typedtree;

namespace Nemerle.OpenCL {

    macro PinObject(var_decl, body)
      syntax( "fixed", "(", var_decl, ")", body )
    {
        match(var_decl) {
            | <[ $(var_name : name) = $var_value ]> => 
                def typer = Macros.ImplicitCTX();
                def var_value_texpr = typer.TypeExpr(var_value);
                def system_array =
                    match(typer.Env.LookupType(["System", "Array"])) {
                        | Some(ty) => FixedType.Class(ty, [])
                        | _ => Message.FatalError("Can't find System.Array class.");
                    }

                def make_fixed_block(addr) {
                    <[
                        def obj = $var_value;
                        def handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
                        try {
                            def $(var_name : name) = $addr;
                            $body
                        } finally {
                            handle.Free();
                        }
                    ]>
                }

                typer.DelayMacro(fun(last_try) {
                    match(var_value_texpr.Type.Hint) {
                        | Some(ft) when ft.TryRequire(system_array) =>
                            <[ Marshal.UnsafeAddrOfPinnedArrayElement(obj, 0) ]>
                                |> make_fixed_block
                                |> Some
                        | Some(_)
                        | _ when last_try =>
                            <[ handle.AddrOfPinnedObject() ]>
                                |> make_fixed_block
                                |> Some
                        | _ =>
                            None()
                    }
                })
           | _ =>
                Message.FatalError(
                    "Invalid usage of `fixed' statement. "
                    "Usage: `fixed(var = foo) { bar(); baz(); }'");
        }
    }

}